package com.panopset;

import static com.panopset.Util.dspmsg;
import static com.panopset.Util.log;
import static com.panopset.Util.x;

import java.io.StringWriter;

import com.panopset.centralen.util.MathUtil;
import com.panopset.security.Encrypter;

/**
 * This is an ASCII text scrambler. Some applications, such as email, don't
 * function very well with binary level encryption.
 *
 * @author Karl Dinwiddie
 *
 */
public final class TextScrambler {

    /**
     * Default number of columns is 80.
     */
    private static final int DEFAULT_COLUMNS = 80;

    /**
     * Columns.
     */
    private static int cols = DEFAULT_COLUMNS;

    /**
     * @param columns Number of columns to present scrambled text in.
     */
    public static void setColumns(final int columns) {
        cols = columns;
    }

    /**
     * @param password
     *            Password.
     */
    private TextScrambler(final String password) {
        this.pwd = password;
    }

    /**
     * Scramble a message.
     *
     * @param msg
     *            Message.
     * @param pwd
     *            Password.
     * @return Scrambled text.
     */
    public static String scramble(final String msg, final String pwd) {
        String rtn = s(msg, pwd, true);
        if (cols > 0) {
            StringWriter sw = new StringWriter();
            int i = 0;
            for (char c : rtn.toCharArray()) {
                sw.append(c);
                i++;
                if (i > cols) {
                    sw.append(Strings.getEol());
                    i = 0;
                }
            }
            return sw.toString();
        }
        return rtn;
    }

    /**
     * Unscramble a message.
     *
     * @param msg
     *            Message.
     * @param pwd
     *            Password.
     * @return Unscrambled text.
     */
    public static String unscramble(final String msg, final String pwd) {
        return s(
                msg.replace(Strings.CARRIAGE_RETURN, "")
                   .replace(Strings.LINE_FEED, ""),
                pwd, false);
    }

    /**
     * Password.
     */
    private final String pwd;

    /**
     * No message message.
     */
    private static final String NO_MESSAGE = x("No"
            + " plain text message supplied.");

    /**
     * No password message.
     */
    private static final String NO_PASSWORD = x("Please select a password.");

    /**
     * Password too short message.
     */
    private static final String PASSWORD_TO_SHORT = x("Password"
            + " should be at least 4 characters long.");

    /**
     * Morph a message.
     *
     * @param msg
     *            Message.
     * @param pwd
     *            Password.
     * @param b
     *            Direction.
     * @return Morphed message.
     */
    private static String s(final String msg,
            final String pwd, final boolean b) {

        if (msg == null) {
            Alert.yellow(NO_MESSAGE);
            return "";
        }

        if (msg.length() < 1) {
            Alert.yellow(NO_MESSAGE);
            return "";
        }

        if (pwd == null) {
            Alert.red(NO_PASSWORD);
            return "";
        }

        if (pwd.length() < MathUtil.FOUR) {
            Alert.red(PASSWORD_TO_SHORT);
            return "";
        }

        dspmsg("");

        try {
            if (b) {
                Alert.yellow(x("scrambling..."));
            } else {
                Alert.yellow(x("unscrambling..."));
            }
            String rtn = new TextScrambler(pwd).morph(msg, b);
            Alert.clearYellow();
            if (b) {
                Alert.green(x("Scramble complete."));
            } else {
                Alert.green(x("Unscramble complete."));
            }
            return rtn;
        } catch (Exception e) {
            log(e);
            Alert.red(x("See log, most errors due to incorrect password."));
            return "";
        }
    }

    /**
     * morph msg.
     *
     * @param msg
     *            msg to morph.
     * @param b
     *            true means encrypt, false decrypt.
     * @return morphed value.
     * @throws Exception
     *             Exception.
     */
    private String morph(final String msg, final boolean b) throws Exception {
        Encrypter encrypter = new Encrypter(pwd);
        if (b) {
            return encrypter.encrypt(msg);
        }
        return encrypter.decrypt(msg);
    }
}
